package com.codefalse.mybatis.generator.controller;

import com.alibaba.fastjson.JSON;
import com.codefalse.mybatis.generator.CodefalseApplication;
import com.codefalse.mybatis.generator.model.CacheModel;
import com.codefalse.mybatis.generator.model.ConfigModel;
import com.codefalse.mybatis.generator.model.GenerateConfigModel;
import com.codefalse.mybatis.generator.model.TableModel;
import com.codefalse.mybatis.generator.listener.DatabaseChangeListener;
import com.codefalse.mybatis.generator.listener.DatabaseClickListener;
import com.codefalse.mybatis.generator.listener.SubmitConfigNotify;
import com.codefalse.mybatis.generator.util.SQLiteUtils;
import com.codefalse.mybatis.generator.view.ConnectionView;
import com.codefalse.mybatis.generator.view.GenerateDialogView;
import com.codefalse.mybatis.generator.view.HomeView;
import de.felixroske.jfxsupport.FXMLController;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;

import javax.sql.DataSource;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * @author songxiangfeng
 * Created At 2019-12-10 16:23
 */
@FXMLController
@Slf4j
public class HomeController implements Initializable, ChangeListener<Boolean>, SubmitConfigNotify, DatabaseChangeListener {

    private DirectoryChooser chooser = null;
    private Boolean isAllTableSelected = false;
    private Boolean isSaveConfig = true;
    private static List<Map<String, Object>> projectPaths;

    @FXML
    private TreeView menuTreeView;
    @FXML
    private TableView<TableModel> tableView;
    @FXML
    private ComboBox projectPathComboBox;
    @FXML
    private TextField projectJavaPathField;
    @FXML
    private TextField projectXmlPathField;
    @FXML
    private Label configMessageLabel;
    @FXML
    private TextField modelPackageField;
    @FXML
    private TextField mapperPackageField;
    @FXML
    private TextField extMapperPackageField;
    @FXML
    private TextField xmlPackageField;
    @FXML
    private TextField extXmlPackageField;
    //更多
    @FXML
    private ComboBox<String> runtimeComboBox;
    @FXML
    private CheckBox commentsCheckBox;
    @FXML
    private CheckBox serializableCheckBox;
    @FXML
    private CheckBox lombokCheckBox;
    @FXML
    private CheckBox mapperCheckBox;

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private Map<String, JdbcTemplate> jdbcTemplateMap;
    @Autowired
    private HomeView homeView;
    @Autowired
    private Map<String, GenerateConfigModel> generateConfigMap;
    @Autowired
    private GenerateDialogController dialogController;
    @Autowired
    private ConfigModel configModel;
    @Autowired
    private Map<String, CacheModel> cacheModelMap;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        //初始化文件夹选择
        chooser = new DirectoryChooser();
        //TreeView配置
        menuTreeView.addEventFilter(MouseEvent.MOUSE_CLICKED, new DatabaseClickListener(this));
        menuTreeView.setRoot(new TreeItem());
        menuTreeView.setShowRoot(false);
        // 查询配置列表
        List<String> nameList = SQLiteUtils.selectConnectionNames();
        for (String name : nameList) {
            insertConfigTreeItem(name);
        }
        //初始化tableView
        this.initTableView();
        //初始化targetRuntime
        this.initRuntime();
    }

    private void initRuntime() {
        List<String> runtimeList = new ArrayList<>(5);
        runtimeList.add("MyBatis3DynamicSql");
        runtimeList.add("MyBatis3Kotlin");
        runtimeList.add("MyBatis3");
        runtimeList.add("MyBatis3Simple");
        runtimeList.add("MyBatis3DynamicSqlV1");
        runtimeComboBox.getItems().addAll(runtimeList);
        runtimeComboBox.getSelectionModel().select("MyBatis3");
    }

    /**
     * 打开新建连接窗口
     */
    public void openConnectionDialog() {
        CodefalseApplication.showView(ConnectionView.class, Modality.APPLICATION_MODAL);
    }

    /**
     * 退出
     */
    public void quitApp() {
        System.out.println("quit");
    }

    /**
     * 保存配置并生成代码
     */
    public void onGenerateAction() {
        GenerateConfigModel generateConfigModel = generateConfigMap.get(configModel.getDatabase());
        if (generateConfigModel == null) {
            generateConfigModel = new GenerateConfigModel();
        }


        String projectPath = projectPathComboBox.getValue().toString();
        if (StringUtils.isBlank(projectPath)) {
            configMessageLabel.setText("项目路径不能为空");
            return;
        }
        generateConfigModel.setProjectPath(projectPath);

        String javaPath = projectJavaPathField.getText();
        generateConfigModel.setJavaPath(javaPath);

        String resourcePath = projectXmlPathField.getText();
        generateConfigModel.setResourcePath(resourcePath);

        String modelPackage = modelPackageField.getText();
        generateConfigModel.setModelPackage(modelPackage);
        String mapperPackage = mapperPackageField.getText();
        String extMapperPackage = extMapperPackageField.getText();
        generateConfigModel.setMapperPackage(mapperPackage);
        generateConfigModel.setExtMapperPackage(extMapperPackage);
        String xmlPackage = xmlPackageField.getText();
        String extXmlPackage = extXmlPackageField.getText();
        generateConfigModel.setXmlPackage(xmlPackage);
        generateConfigModel.setExtXmlPackage(extXmlPackage);
        //更多配置
        generateConfigModel.setTargetRuntime(runtimeComboBox.getSelectionModel().getSelectedItem());
        generateConfigModel.setSuppressAllComments(commentsCheckBox.isSelected());
        generateConfigModel.setSupportSerial(serializableCheckBox.isSelected());
        generateConfigModel.setSupportLombok(lombokCheckBox.isSelected());
        generateConfigModel.setAnnotationMapper(mapperCheckBox.isSelected());

        List<TableModel> tableModels = tableView.getSelectionModel().getSelectedItems();
        if (tableModels == null || tableModels.size() == 0) {
            configMessageLabel.setText("请先选择要生成的表");
            return;
        }
        generateConfigModel.setTableModelList(tableModels);
        generateConfigMap.put(configModel.getDatabase(), generateConfigModel);

        if (isSaveConfig) {
            SQLiteUtils.saveOrUpdateProject(configModel.getName(), configModel.getDatabase(), generateConfigModel);
        }

        //打开去确认弹出框
        GenerateDialogView dialogView = applicationContext.getBean(GenerateDialogView.class);
        Scene scene = dialogView.getView().getScene();
        if (scene == null) {
            scene = new Scene(dialogView.getView());
        }
        Stage stage = new Stage();
        stage.setScene(scene);
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.initOwner(CodefalseApplication.getStage());
        stage.setTitle("通知");
        stage.setOnShown(dialogController);
        stage.showAndWait();
    }

    /**
     * 生成代码的时候，是否保存配置
     * @param event
     */
    public void onSaveConfigAction(ActionEvent event) {
        CheckBox checkBox = (CheckBox)event.getTarget();
        isSaveConfig = checkBox.isSelected();
    }

    /**
     * 忽略选择，直接生成所有表
     * @param event
     */
    public void onSelectGenerateAllAction(ActionEvent event) {
        CheckBox checkBox = (CheckBox)event.getTarget();
        isAllTableSelected = checkBox.isSelected();
    }

    /**
     * 点击文本框事件
     * @param event
     */
    public void onFieldMouseClicked(MouseEvent event) {
        String projectPath = projectPathComboBox.getValue().toString();
        if (StringUtils.isBlank(projectPath)) {
            configMessageLabel.setText("请先选择项目路径");
            return;
        }
        File file = chooser.showDialog(homeView.getView().getScene().getWindow());
        if (file == null) {
            return;
        }
        String path = file.getPath();
        path = path.substring(projectPath.length());
        TextField field = (TextField)event.getSource();
        field.setText(path);
    }

    /**
     * 选择已有项目
     */
    public void onChangeProjectPathAction() {
        int index = projectPathComboBox.getSelectionModel().getSelectedIndex();
        Map<String, Object> selectMap = projectPaths.get(index);
        String config = selectMap.get("config").toString();
        GenerateConfigModel model = JSON.parseObject(config, GenerateConfigModel.class);
        projectJavaPathField.setText(model.getJavaPath());
        projectXmlPathField.setText(model.getResourcePath());
        modelPackageField.setText(model.getModelPackage());
        mapperPackageField.setText(model.getMapperPackage());
        extMapperPackageField.setText(model.getExtMapperPackage());
        xmlPackageField.setText(model.getXmlPackage());
        extXmlPackageField.setText(model.getExtXmlPackage());
        commentsCheckBox.setSelected(model.getSuppressAllComments());
        serializableCheckBox.setSelected(model.getSupportSerial());
        lombokCheckBox.setSelected(model.getSupportLombok());
        mapperCheckBox.setSelected(model.getAnnotationMapper());
    }
    /**
     * 选择项目路径
     */
    public void onSelectProjectPathAction() {
        File file = chooser.showDialog(homeView.getView().getScene().getWindow());
        if (file == null) {
            projectPathComboBox.setValue("");
            return;
        }
        configMessageLabel.setText("");
        chooser.setInitialDirectory(file);
        projectPathComboBox.getItems().add(file.getPath());
        projectPathComboBox.getSelectionModel().select(projectPathComboBox.getItems().size() - 1);
    }

    @Override
    public void insertConfigTreeItem(String name) {
        TreeItem configTreeItem = new TreeItem(name);
        configTreeItem.expandedProperty().addListener(this);

        configTreeItem.getChildren().add(new TreeItem<>("scheme(0)"));
        menuTreeView.getRoot().getChildren().add(configTreeItem);

        //增加缓存
        if (!cacheModelMap.containsKey(name)) {
            cacheModelMap.put(name, new CacheModel());
        }
    }

    @Override
    public void currentDatabase(String configName, String database) {
        projectPaths = SQLiteUtils.selectProjects(configName, database);
        this.initProjectPathComboBox();



        configModel.setDatabase(database);
        JdbcTemplate jdbcTemplate = jdbcTemplateMap.get(configName);
        StringBuilder dbSB = new StringBuilder("use");
        dbSB.append(" ");
        dbSB.append("`").append(database).append("`");
        jdbcTemplate.execute(dbSB.toString());
        List<String> tableList = jdbcTemplate.queryForList("show tables", String.class);
        //清空
        tableView.getItems().clear();
        for (String table : tableList) {
            TableModel model = new TableModel();
            model.setTable(table);
            //查询表主键
            String keySql = "SELECT column_name as primary_key FROM information_schema.`KEY_COLUMN_USAGE` WHERE CONSTRAINT_NAME='PRIMARY' AND CONSTRAINT_SCHEMA=? AND table_name=?";

            String primaryKey = "";
            try {
                primaryKey = jdbcTemplate.queryForObject(keySql, String.class, database, table);
            } catch (Exception e) {}
            model.setPrimaryKey(primaryKey);

            tableView.getItems().add(model);
        }
    }

    /**
     * 展开连接配置
     * @param observable
     * @param oldValue
     * @param newValue
     */
    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
        TreeItem configTreeItem = (TreeItem)((BooleanPropertyBase)observable).getBean();
        String configName = configTreeItem.getValue().toString();
        log.debug("当前配置：{}, 是否展开:{}", configName, newValue);
        if (newValue) {
            TreeItem treeItem = (TreeItem) configTreeItem.getChildren().get(0);
            //检查缓存中是否存在数据库列表
            CacheModel cacheModel = cacheModelMap.get(configName);
            if (cacheModel.getDatabases() != null) {
                fillConfigTreeItem(treeItem, cacheModel.getDatabases());
                return;
            }
            //查询连接下数据库
            JdbcTemplate jt = jdbcTemplateMap.get(configName);
            if (jt == null) {
                String sql = "select * from `connections` where `name` = ?";
                Map<String, Object> map = jdbcTemplate.queryForMap(sql, configName);
                configModel.setName(configName);
                configModel.setHost(map.get("host").toString());
                configModel.setPort((Integer) map.get("port"));
                configModel.setUsername(map.get("username").toString());
                configModel.setPassword(map.get("password").toString());
                configModel.setArgs(map.getOrDefault("args", "").toString());

                DataSource dataSource = DataSourceBuilder.create().type(SingleConnectionDataSource.class)
                        .driverClassName("com.mysql.jdbc.Driver")
                        .url(configModel.packageJDBCUrl())
                        .username(configModel.getUsername())
                        .password(configModel.getPassword())
                        .build();
                jt = new JdbcTemplate(dataSource);
                jdbcTemplateMap.put(configName, jt);
            }
            try {
                List<String> list = jt.queryForList("show databases", String.class);
                cacheModel.setDatabases(list);
                fillConfigTreeItem(treeItem, list);
            } catch (Exception e) {
                log.error("遍历数据库失败：{}", e.getMessage());
                configTreeItem.setExpanded(false);
            }
        }
    }

    private void fillConfigTreeItem(TreeItem treeItem, List<String> databases) {
        treeItem.getChildren().clear();
        treeItem.setValue("schema(" + databases.size() + ")");
        for (String database : databases) {
            treeItem.getChildren().add(new TreeItem<>(database));
        }
        treeItem.setExpanded(true);
    }

    /**
     * 初始化TableView
     */
    private void initTableView() {
        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        tableView.setEditable(true);
        //表名称
        TableColumn<TableModel, String> tableColumn = new TableColumn<>("Table");
        tableColumn.setCellValueFactory(new PropertyValueFactory<>("table"));
        tableColumn.setPrefWidth(150);
        tableView.getColumns().add(tableColumn);
        //表主键
        TableColumn<TableModel, String> primaryKeyColumn = new TableColumn<>("PrimaryKey");
        primaryKeyColumn.setCellValueFactory(new PropertyValueFactory<>("primaryKey"));
        tableView.getColumns().add(primaryKeyColumn);
        //DML
        TableColumn dmlColumn = new TableColumn("DML");
        //enableInsert
        TableColumn<TableModel, Boolean> enableInsertColumn = new TableColumn<>("Insert");
        enableInsertColumn.setEditable(true);
        enableInsertColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enableInsertColumn));
        enableInsertColumn.setCellValueFactory(new PropertyValueFactory<>("enableInsert"));
        //enableDelete
        TableColumn<TableModel, Boolean> enableDeleteColumn = new TableColumn<>("Delete");
        enableDeleteColumn.setEditable(true);
        enableDeleteColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enableDeleteColumn));
        enableDeleteColumn.setCellValueFactory(new PropertyValueFactory<>("enableDelete"));
        //enableUpdate
        TableColumn<TableModel, Boolean> enableUpdateColumn = new TableColumn<>("Update");
        enableUpdateColumn.setEditable(true);
        enableUpdateColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enableUpdateColumn));
        enableUpdateColumn.setCellValueFactory(new PropertyValueFactory<>("enableUpdate"));
        //enableSelect
        TableColumn<TableModel, Boolean> enableSelectColumn = new TableColumn<>("Select");
        enableSelectColumn.setEditable(true);
        enableSelectColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enableSelectColumn));
        enableSelectColumn.setCellValueFactory(new PropertyValueFactory<>("enableSelect"));
        dmlColumn.getColumns().addAll(enableInsertColumn, enableDeleteColumn, enableUpdateColumn, enableSelectColumn);
        tableView.getColumns().add(dmlColumn);
        //是否生成Example
        TableColumn exampleColumn = new TableColumn<>("Example");
        // example update
        TableColumn<TableModel, Boolean> updateExampleTableColumn= new TableColumn<>("Update");
        updateExampleTableColumn.setEditable(true);
        updateExampleTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(updateExampleTableColumn));
        updateExampleTableColumn.setCellValueFactory(new PropertyValueFactory<>("exampleUpdate"));
        // example select
        TableColumn<TableModel, Boolean> selectExampleTableColumn= new TableColumn<>("Select");
        selectExampleTableColumn.setEditable(true);
        selectExampleTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(selectExampleTableColumn));
        selectExampleTableColumn.setCellValueFactory(new PropertyValueFactory<>("exampleSelect"));
        // example delete
        TableColumn<TableModel, Boolean> deleteExampleTableColumn= new TableColumn<>("Delete");
        deleteExampleTableColumn.setEditable(true);
        deleteExampleTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(deleteExampleTableColumn));
        deleteExampleTableColumn.setCellValueFactory(new PropertyValueFactory<>("exampleDelete"));
        // example count
        TableColumn<TableModel, Boolean> countExampleTableColumn= new TableColumn<>("Count");
        countExampleTableColumn.setEditable(true);
        countExampleTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(countExampleTableColumn));
        countExampleTableColumn.setCellValueFactory(new PropertyValueFactory<>("exampleCount"));

        exampleColumn.getColumns().addAll(updateExampleTableColumn, selectExampleTableColumn, deleteExampleTableColumn, countExampleTableColumn);
        tableView.getColumns().add(exampleColumn);
    }

    /**
     * 初始化项目目录
     */
    private void initProjectPathComboBox() {
        projectPathComboBox.getItems().clear();
        for (Map<String, Object> map : projectPaths) {
            projectPathComboBox.getItems().add(map.get("path"));
        }
    }
}
